package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.heima.apis.article.IArticleClient;
import com.heima.common.exception.CustomException;
import com.heima.common.redis.CacheService;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.BaiDuScanService;
import com.heima.wemedia.service.WmAutoScanService;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import org.codehaus.jackson.map.util.BeanUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author jack
 * @data 2023 13:39
 */
@Service
@Transactional
@Slf4j
public class WmAutoScanServiceImpl implements WmAutoScanService {

    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Autowired
    private BaiDuScanService baiDuScanService;
    @Autowired
    private IArticleClient articleClient;
    @Autowired
    private WmUserMapper wmUserMapper;
    @Autowired
    private WmChannelMapper wmChannelMapper;
    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private FileStorageService fileStorageService;

    @Override
    public void scanNews(Integer newsId) {
        //1. 查询自媒体文章
        WmNews wmNews = wmNewsMapper.selectById(newsId);
        if (wmNews == null)
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);

        //1.1 提取出文章中的文本和图片
        /*{"text"="黑马头条项目背景黑马头条随着只能手机....",
            "img"=["http://192...","http://192..."]
            }
         */
        Map<String,Object> map = handleTextAndImg(wmNews);

        //2. 审核文章的文本内容
        boolean isTextPass = scanText(map.get("text").toString(),wmNews);
        if (!isTextPass)return;

        //3. 审核文章的图片内容
        boolean isImgPass = scanImg((List<String>) map.get("img"),wmNews);
        if (!isImgPass)return;

        //4. 都审核通过：远程调用article微服务，进行app_article文章信息的保存
        saveArticle(wmNews);
    }

    //远程调用article微服务，进行app_article文章信息的保存
    private void saveArticle(WmNews wmNews) {
        ArticleDto dto = new ArticleDto();
        //给dto的属性进行值的填充
        if (wmNews.getArticleId() != null){
            dto.setId(wmNews.getArticleId());
        }
        BeanUtils.copyProperties(wmNews,dto);
        //1. 以下字段无法自动填充，需要手动处理
        //作者id
        dto.setAuthorId(Long.valueOf(wmNews.getUserId()));
        //作者名称
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (wmUser != null){
            dto.setAuthorName(wmUser.getName());
        }
        //频道名称
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if (wmChannel != null){
            dto.setChannelName(wmChannel.getName());
        }
        //文章布局
        dto.setLayout(wmNews.getType());

        //向article微服务发起远程调用  -- sentinel
        ResponseResult responseResult = articleClient.saveArticle(dto);   //假设article服务瘫痪了，这里就需要进行熔断降级
        //将返回的ap_article 的id回填到wm_news表的articleId上
        if (responseResult.getCode().equals(200)){
            String articleId = responseResult.getData().toString();
            wmNews.setArticleId(Long.valueOf(articleId));
            //修改文章状态为：已发布
            updateNewsStatus(wmNews,WmNews.Status.PUBLISHED.getCode(),"审核成功");
        }
    }

    //封装一个方法：实现OCR图文识别
    private String doOCR(String imgUrl){
        try {
            //基于MinIO下载图片
            byte[] bytes = fileStorageService.downLoadFile(imgUrl);
            InputStream is = new ByteArrayInputStream(bytes);
            //创建Tesseract对象
            ITesseract tesseract = new Tesseract();
            //设置字体库路径
            tesseract.setDatapath("D:\\IdeaProjects\\heima-leadnews\\heima-leadnews-service\\heima-leadnews-wemedia\\src\\main\\resources\\tessdata");
            //中文识别
            tesseract.setLanguage("chi_sim");
            //执行ocr识别
            BufferedImage bufferedImage = ImageIO.read(is);
            String result = tesseract.doOCR(bufferedImage);
            //替换回车和tal键  使结果为一行
            result = result.replaceAll("\\r|\\n","-").replaceAll(" ","");
            log.info("识别的结果为："+result);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    //图片审核逻辑
    private boolean scanImg(List<String> imgs, WmNews wmNews) {
        if (imgs != null && imgs.size() > 0){
            //进行图文识别（ocr）并审核文字
            for (String img : imgs) {
                if (!StringUtils.isEmpty(img)) {
                    String textInImg = doOCR(img);
                    if (!StringUtils.isEmpty(textInImg)) {
                        //存在文字：审核文字
                        boolean isPass = scanText(textInImg, wmNews);
                        if (!isPass) {
                            return false;
                        }
                    }

                    //调用百度云来审核图片
                    Map map = baiDuScanService.greenScanImg(img);
                    Object conclusionType = map.get("conclusionType");
                    int conclusionTypeCode = conclusionType == null ? 3 : Integer.valueOf(conclusionType.toString());
                    if (conclusionTypeCode == 2 || conclusionTypeCode == 4) {
                        //不合规| 审核失败
                        //修改文章状态为：2审核失败，给出原因：审核失败：图片内容不合法!
                        updateNewsStatus(wmNews, WmNews.Status.FAIL.getCode(), "审核失败：图片内容不合法!");
                        return false;
                    }
                    if (conclusionTypeCode == 3) {
                        //疑似
                        //修改文章状态为：3人工审核，给出原因：待人工审核：图片内容疑似违规!
                        updateNewsStatus(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "待人工审核：图片内容疑似违规!");
                        return false;
                    }
                }
            }
        }
        return true;
    }

    //文本审核逻辑
    private boolean scanText(String text,WmNews wmNews) {
        //敏感词先内审
        //1. 查出数据库中所有敏感词：wm_sensitive
        String sensitivesJSON = cacheService.get("sensitives");
        List<WmSensitive> wmSensitives = JSON.parseArray(sensitivesJSON,WmSensitive.class);
        if (wmSensitives == null || wmSensitives.size() < 1) {
            wmSensitives = wmSensitiveMapper.selectList(null);
            cacheService.set("sensitives",JSON.toJSONString(wmSensitives));
        }

        //2. 提取出所有敏感词来初始化DFA词库
        List<String> sensitives = wmSensitives.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
        SensitiveWordUtil.initMap(sensitives);

        //3. 基于SensitiveWordUtil来进行敏感词匹配
        Map<String, Integer> resMap = SensitiveWordUtil.matchWords(text);
        if (resMap != null && resMap.size() > 0){
            //自管理敏感词命中了
            updateNewsStatus(wmNews,WmNews.Status.FAIL.getCode(),"审核失败：文本内容不合法!");
            return false;
        }

        //再外审
        Map textResultMap = baiDuScanService.greenScanText(text);
        Object conclusionType = 3;
        if (textResultMap != null) {
            conclusionType = textResultMap.get("conclusionType");
        }
        int conclusionTypeCode = Integer.valueOf(conclusionType.toString());
        if (conclusionTypeCode == 1)return true;
        if (conclusionTypeCode == 2 || conclusionTypeCode == 4){
            //不合规| 审核失败
            //修改文章状态为：2审核失败，给出原因：审核失败：文本内容不合法!
            updateNewsStatus(wmNews,WmNews.Status.FAIL.getCode(),"审核失败：文本内容不合法!");
            return false;
        }
        if (conclusionTypeCode == 3){
            //疑似
            //修改文章状态为：3人工审核，给出原因：待人工审核：文本内容疑似违规!
            updateNewsStatus(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"待人工审核：文本内容疑似违规!");
            return false;
        }

        return false;
    }

    //修改文章状态
    private void updateNewsStatus(WmNews wmNews,Short status,String reason){
        wmNews.setStatus(status);
        wmNews.setReason(reason);
        wmNewsMapper.updateById(wmNews);
    }

    //提取文章中所有文字和图片信息
    private Map<String, Object> handleTextAndImg(WmNews wmNews) {
        //定义一个StringBuilder容器来装所有文字
        StringBuilder sb = new StringBuilder(wmNews.getTitle());
        //定义一个ArrayList容器来装所有图片
        List<String> list = new ArrayList<>();
        //定义一个Map容器来放文字和图片作为结果返回
        Map<String,Object> resultMap = new HashMap<>();

        //提取文字追加到sb
        sb.append(wmNews.getLabels());
        JSONArray jsonArray = JSONArray.parseArray(wmNews.getContent());
        for (Object obj : jsonArray) {
            JSONObject jsonObject = JSONObject.parseObject(obj.toString());
            if ("text".equals(jsonObject.getString("type"))){
                sb.append(jsonObject.getString("value"));
            }

            //提取图片追加到list
            if ("image".equals(jsonObject.getString("type"))){
                list.add(jsonObject.getString("value"));
            }
        }
        //封面图片也要收集
        Collections.addAll(list,wmNews.getImages().split(","));

        //图片去重
        list = list.stream().distinct().collect(Collectors.toList());

        //将sb和list放到resultMap并返回
        resultMap.put("text",sb.toString());
        resultMap.put("img",list);
        return resultMap;
    }
}
